package cn.iocoder.yudao.module.vote.aop;

import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.module.vote.constants.RedisConst;
import cn.iocoder.yudao.module.vote.utils.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.vote.enums.ErrorCodeConstants.USER_RESULT_NOT_EXISTS;
import static cn.iocoder.yudao.module.vote.enums.ErrorCodeConstants.VOTE_SUBMIT_REPEAT;

/**
 * @description:
 * @author: moving
 * @date: 2022/3/21 17:04
 */
@Slf4j
@Aspect
@Component
public class SubmitCheckAspect {

    @Autowired
    private RedisService redisService;

    @Resource
    private RedissonClient redissonClient;

    /**
     * 定义切点
     */
    @Pointcut("@annotation(cn.iocoder.yudao.module.vote.aop.SubmitCheck)")
    public void preventDuplication() {
    }


    /**
     * 环绕通知 （可以控制目标方法前中后期执行操作，目标方法执行前后分别执行一些代码）
     *
     * @param joinPoint
     * @return
     */
    @Around("preventDuplication()")
    public Object before(ProceedingJoinPoint joinPoint) throws Exception {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        Assert.notNull(request, "request cannot be null.");

        //获取执行方法
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        //获取防重复提交注解
        SubmitCheck annotation = method.getAnnotation(SubmitCheck.class);



        // 获取ip地址以及方法标记，生成redisKey和redisValue
        String key = getKey(method, ServletUtil.getClientIP(request, null));
        //设置防重复操作限时标记（前置通知）
        if (redisService.acquireLock(key,annotation.expireSeconds())) {
            try {
                //正常执行方法并返回
                return joinPoint.proceed();
            } catch (Throwable throwable) {
                ServiceException ex = (ServiceException)throwable;
                redisService.deleteObject(key);
                //确保方法执行异常实时释放限时标记(异常后置通知)
                ErrorCode errorCode = new ErrorCode(ex.getCode(),ex.getMessage());
                throw exception(errorCode);
            }finally {
                redisService.releaseLock(key);
            }
        } else {
            throw exception(VOTE_SUBMIT_REPEAT);
        }
    }


    public String getKey(Method method, String ip) {
        String key = StrUtil.format(RedisConst.VOTE_ONLINE_SUBMIT,method.getName(),ip);
        return key;
    }

}
